
#include <BImage>
#include <BStyle>
#include <BPainter>
#include <BOpenGL>
#include <BMouseEvent>
#include <BButton>
#include <BLayout>

#include "member_BGroupBox.h"
#include "member_BWidget.h"
#include "member_BStyle.h"

using namespace BWE;

#define member					(*(member_BGroupBox*)_ptr)
#define member_allocate()		_ptr = new member_BGroupBox(this)
#define member_release()		delete (member_BGroupBox*)_ptr

BGroupBox::BGroupBox(BWidget* parent) : BWidget(parent)
{
	member_allocate();
}
BGroupBox::BGroupBox(const BString& title, BWidget* parent) : BWidget(parent)
{
	member_allocate();
	this->setTitle(title);
}
BGroupBox::~BGroupBox()
{
	member_release();
}

bool BGroupBox::setTitle(const BString& title)
{
	if (BWidget::setTitle(title))
	{
		member.dirty = true;
		this->fresh();
		return true;
	}
	return false;
}
bool BGroupBox::setIcon(BIcon* icon)
{
	if (BWidget::setIcon(icon))
	{
		member.dirty = true;
		this->fresh();
		return true;
	}
	return false;
}

void BGroupBox::setTitleVisible(bool titleVisible)
{
	if (member.titleVisible != titleVisible)
	{
		member.titleVisible = titleVisible;
		member.dirty = true;
		this->fresh();
	}
}
bool BGroupBox::titleVisible() const
{
	return member.titleVisible;
}

void BGroupBox::setTitleAlign(Align titleAlign)
{
	if (member.ia_align != titleAlign)
	{
		member.ia_align = titleAlign;
		member.dirty = true;
		this->emit(Signal_AlignChanged, titleAlign);
		this->fresh();
	}
}
Align BGroupBox::titleAlign() const
{
	return member.ia_align;
}

void BGroupBox::setTitleForm(Form titleForm)
{
	if (member.ia_form != titleForm)
	{
		member.ia_form = titleForm;
		member.dirty = true;
		this->emit(Signal_FormChanged, titleForm);
		this->adjust();
	}
}
Form BGroupBox::titleForm() const
{
	return member.ia_form;
}

void BGroupBox::setFlat(bool flat)
{
	if (member.flat != flat)
	{
		member.flat = flat;
		member.dirty = true;
		this->emit(Signal_Flat, flat);
		this->fresh();
	}
}
bool BGroupBox::flat() const
{
	return member.flat;
}

void BGroupBox::setWired(bool wired)
{
	if (member.wired != wired)
	{
		member.wired = wired;
		member.dirty = true;
		this->emit(Signal_Wired, wired);
		this->fresh();
	}
}
bool BGroupBox::wired() const
{
	return member.wired;
}

void BGroupBox::setFolded(bool folded)
{
	if (member.folded != folded)
	{
		member.folded = folded;
		if (folded)
		{
			member.backupPerch = this->perch();
			this->setPerch(Part_Bottom, 0);
			member.backupMargin = this->margin();
			this->setMargin(0);
			member.backupVPolicy = this->heightPolicy();
			this->setHeightPolicy(Policy_Preferred);
		}
		else
		{
			this->setPerch(member.backupPerch);
			this->setMargin(member.backupMargin);
			this->setHeightPolicy(member.backupVPolicy);
		}
		member.dirty = true;
		this->emit(Signal_Folded, member.folded);
		this->fresh();
	}
}
bool BGroupBox::folded() const
{
	return member.folded;
}

void BGroupBox::setCheckable(bool checkable)
{
	if (member.checkable != checkable)
	{
		member.checkable = checkable;
		member.checked = false;
		this->emit(Signal_Checkable, checkable);
		this->fresh();
	}
}
bool BGroupBox::checkable() const
{
	return member.checkable;
}

void BGroupBox::setChecked(bool checked)
{
	if (member.checkable)
	{
		if (member.checked != checked)
		{
			member.checked = checked;
			emit(Signal_Checked, checked);
			this->fresh();
		}
	}
}
bool BGroupBox::checked() const
{
	return member.checked;
}

BSize BGroupBox::sizeHint() const
{
	if (member.dirty)
	{
		member.freshRects();
		member.dirty = false;
	}
	int hintWidth = member.ia_hintSize.width();
	if (member.folded)
	{
		if (hintWidth < member.centerSize.width())
			hintWidth = member.centerSize.width();
	}
	return this->preferSize(hintWidth, 0);
}

bool BGroupBox::query(const BString& text, BValue& value) const
{
	if (text == "header-rect")
	{
		value = member.headerRect;
		return true;
	}
	if (text == "title-area")
	{
		value = member.titleRect;
		return true;
	}
	if (text == "text-area")
	{
		value = member.ia_textArea;
		return true;
	}
	if (text == "text-rect")
	{
		value = member.ia_textRect;
		return true;
	}
	if (text == "icon-area")
	{
		value = member.ia_iconArea;
		return true;
	}
	if (text == "icon-rect")
	{
		value = member.ia_iconRect;
		return true;
	}
	return BWidget::query(text, value);
}

void BGroupBox::resizeEvent(const BEvent& event)
{
	BWidget::resizeEvent(event);
	member.dirty = true;
}
void BGroupBox::adjustEvent(const BEvent& event)
{
	BWidget::adjustEvent(event);
	member.dirty = true;
}

void BGroupBox::styleEvent(const BEvent& event)
{
	BWidget::styleEvent(event);
	const BStyle* style = this->realStyle();
	member.titleStyle = style;
	member.titleSize = -1;

	member.autoTop = style->value(Value_Perch_Top).empty();
	member.autoLeft = style->value(Value_Perch_Left).empty();
	member.autoRight = style->value(Value_Perch_Right).empty();
	member.autoBottom = style->value(Value_Perch_Bottom).empty();

	style_member(style)->assign(Value_Form,member.ia_form);
	style_member(style)->assign(Value_Align,member.ia_align);
	style_member(style)->assign(Value_Spacing,member.ia_spacing);
	style_member(style)->assign(Value_Icon_Size,member.iconSize);
	style_member(style)->assign(Value_Wire_Size,member.wireSize);
	style_member(style)->assign(Value_Wire_Stripe, member.wireStripe);

	style_member(style)->assign(Value_Flat, member.flat);
	style_member(style)->assign(Value_Wired, member.wired);

	if (const BStyle* titleStyle = style->annex("title"))
	{
		member.titleStyle = titleStyle;

		style_member(titleStyle)->assign(Value_Visible, member.titleVisible);
		style_member(titleStyle)->assign(Value_Height, member.titleSize);

		style_member(titleStyle)->assign(Value_Form, member.ia_form);
		style_member(titleStyle)->assign(Value_Align, member.ia_align);
		style_member(titleStyle)->assign(Value_Spacing, member.ia_spacing);
		style_member(titleStyle)->assign(Value_Icon_Size, member.titleIconSize);

		style_member(titleStyle)->assign(Value_Margin_Top, member.titleMargin.top());
		style_member(titleStyle)->assign(Value_Margin_Left, member.titleMargin.left());
		style_member(titleStyle)->assign(Value_Margin_Right, member.titleMargin.right());
		style_member(titleStyle)->assign(Value_Margin_Bottom, member.titleMargin.bottom());
	}

	member.dirty = true;
}
void BGroupBox::freshEvent(const BEvent& event)
{
	BWidget::freshEvent(event);
	if (member.dirty)
	{
		member.freshRects();
		member.dirty = false;
	}
}
void BGroupBox::paintEvent(const BEvent& event)
{
	BPainter painter(this);
	painter.drawBackground();
	painter.drawForeground();
	painter.drawPerch();
	painter.drawTitle();

	State state = painter.state();
	if (member.checked)
		state = State_Checked;
	if (member.hovered)
	{
		if (member.pressed)
			state |= State_Pressed;
		else
			state |= State_Hovered;
	}

	if (member.titleVisible && member.ia_area.valid())
	{
		painter.setStyle(member.titleStyle);
		if (member.ia_iconRect.valid())
		{
			BRect imageRect = member.ia_iconRect;
			if (member.iconSize.empty())
				imageRect = this->rect(Part_Top).wrap(imageRect);
			if (BIcon* icon = this->icon())
			{
				if (BImage* image = icon->image(state))
				{
					painter.drawImage(imageRect, image);
				}
			}
			else if (member.checkable)
			{
				painter.setColor(Color_Arrow, state);
				painter.setLineSmooth(true);
				painter.setLineWidth(2);
				painter.drawRect(imageRect % 86);
				if (member.checked)
				{
					imageRect %= 66;
					BPointf p0 = imageRect.leftCenter();
					BPointf p1 = imageRect.bottomCenter();
					BPointf p2 = imageRect.rightTop();
					painter.drawLine(p0, p1);
					painter.drawLine(p1, p2);
				}
			}
		}
		if (painter.setColor(Color_Text, state))
		{
			if (widget_member(this)->t_title.size())
				painter.drawText(member.ia_textRect, widget_member(this)->t_title);
			else
				painter.drawText(member.ia_textRect, widget_member(this)->title);
		}
		painter.setStyle(0);
	}

	if (member.wired && member.wireSize > 0 && painter.setColor(Color_Wire, state))
	{
		painter.setLineSmooth(true);
		painter.setLineWidth(member.wireSize);
		if (member.flat || member.folded)
		{
			painter.drawLine(member.wirePoints.first(), member.wirePoints(1));
			painter.drawLine(member.wirePoints.last(), member.wirePoints(member.wirePoints.size() - 2));
		}
		else
		{
			painter.drawLineStrip(member.wirePoints);
		}
	}

	painter.drawMargin();
	painter.drawBorder();
}

void BGroupBox::hoverEvent(const BEvent& event)
{
	BWidget::hoverEvent(event);
	this->fresh();
}

void BGroupBox::mouseEnterEvent(const BMouseEvent& mouseEvent)
{
	if (member.hovered)
	{
		member.hovered = false;
		this->fresh();
	}
	BWidget::mouseEnterEvent(mouseEvent);
}
void BGroupBox::mouseLeaveEvent(const BMouseEvent& mouseEvent)
{
	if (member.hovered)
	{
		member.hovered = false;
		this->fresh();
	}
	BWidget::mouseLeaveEvent(mouseEvent);
}

void BGroupBox::mousePressEvent(const BMouseEvent& mouseEvent)
{
	BPoint pos = mouseEvent.pos();
	if (mouseEvent.button() && member.hovered)
	{
		member.pressed = true;
		this->fresh();
	}
	BWidget::mousePressEvent(mouseEvent);
}
void BGroupBox::mouseReleaseEvent(const BMouseEvent& mouseEvent)
{
	const BPoint& pos = mouseEvent.pos();
	if (mouseEvent.button() && member.pressed)
	{
		member.pressed = false;
		this->fresh();
	}
	BWidget::mouseReleaseEvent(mouseEvent);
}
void BGroupBox::mouseMoveEvent(const BMouseEvent& mouseEvent)
{
	if (mouseEvent.button() == Button_None)
	{
		const BPoint& pos = mouseEvent.pos();
		bool hovered = member.headerRect.contain(pos);
		if (member.hovered != hovered)
		{
			member.hovered = hovered;
			this->fresh();
		}
	}
	BWidget::mouseMoveEvent(mouseEvent);
}

void BGroupBox::mouseClickEvent(const BMouseEvent& mouseEvent)
{
	if (mouseEvent.button() == Button_Left)
	{
		const BPoint& pos = mouseEvent.pos();
		if (member.checkable && member.headerRect.contain(pos))
		{
			setChecked(!member.checked);
			emit(Signal_Toggled, member.checked);
		}
	}
	BWidget::mouseClickEvent(mouseEvent);
}
